# !/usr/bin/python
# -*- coding: utf-8 -*-

# THIS FILE IS LICENSED UNDER THE MIT LICENSE AS OUTLINED IMMEDIATELY BELOW: 
#
# Permission is hereby granted, free of charge, to any person obtaining a
# copy of this software and associated documentation files (the "Software"),  
# to deal in the Software without restriction, including without limitation  
# the rights to use, copy, modify, merge, publish, distribute, sublicense,  
# and/or sell copies of the Software, and to permit persons to whom the  
# Software is furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in 
# all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 
# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 
# DEALINGS IN THE SOFTWARE.

# actions.py
# written by: 
#     Jan Jokela <jan@devion.org>

"""   
User interface actions, these actions are done for QGL.scene.Group()'s
Currently only 4 are available: Move
"""
__version__ = 0.3
__doc__ = 'ngui actions'

from eye import *
import events
import math



class Move(object):
    """
    Moves a qgl.scene.Group from current position to a new position
    in a certain amount of time (in seconds)
    """
    def __init__(self, group, pos=(0.0,0.0,0.0), time=1.0):
        self.group = group
        self.pos = pos
        actionhandler.subscribe(self)
        xdistance = math.sqrt((self.pos[0] - self.group.translate[0])**2) 
        ydistance = math.sqrt((self.pos[1] - self.group.translate[1])**2) 
        zdistance = math.sqrt((self.pos[2] - self.group.translate[2])**2)
        if xdistance > ydistance:
            if xdistance > zdistance: biggerdistance = xdistance
            else: biggerdistance = zdistance
        else: 
            if ydistance > zdistance: biggerdistance = ydistance
            else: biggerdistance = zdistance
        speed = biggerdistance/fps / time
        if self.pos[0] > self.group.translate[0]: self.xvel = xdistance / biggerdistance * speed
        elif self.pos[0] == self.group.translate[0]: self.xvel = 0.0
        else: self.xvel = -(xdistance / biggerdistance * speed)
        if self.pos[1] > self.group.translate[1]: self.yvel = ydistance / biggerdistance * speed
        elif self.pos[1] == self.group.translate[1]: self.yvel = 0.0
        else: self.yvel = -(ydistance / biggerdistance * speed)
        if self.pos[2] > self.group.translate[2]: self.zvel = zdistance / biggerdistance * speed
        elif self.pos[2] == self.group.translate[2]: self.zvel = 0.0
        else: self.zvel = -(zdistance / biggerdistance * speed)
        
        self.group.velocity = (self.xvel, self.yvel, self.zvel, 0.0)
        self.active = True
        
        if self.pos == self.group.translate: 
            self.stop()
    def execute(self):
        if self.xvel > 0 and self.group.translate[0] + self.xvel > self.pos[0]: self.stop()
        elif self.xvel < 0 and self.group.translate[0] + self.xvel < self.pos[0]: self.stop()
        elif self.yvel > 0 and self.group.translate[1] + self.yvel > self.pos[1]: self.stop()
        elif self.yvel < 0 and self.group.translate[1] + self.yvel < self.pos[1]: self.stop()
        elif self.zvel > 0 and self.group.translate[2] + self.zvel > self.pos[2]: self.stop()
        elif self.zvel < 0 and self.group.translate[2] + self.zvel < self.pos[2]: self.stop()
    def stop(self):
        self.group.translate = self.pos 
        self.group.velocity = (0.0, 0.0, 0.0, 0.0)
        self.active = False
        actionhandler.unsubscribe(self)        
    def finished(self):
        if not self.active: return True
        else: return False
        
            
class Fade(object):
    """
    Fades the color from current rgba values to desired rgba values in a
    certain amount of time
    obs: unlike other effects, we won't be receiving the qgl.scene.Group
    as an arg but the qgl.scene.color applied to the group
    """
    def __init__(self, color, red=1.0, green=1.0, blue=1.0, alpha=1.0, time=1.0):
        # the fade effect works like the move effect, same logic
        self.color = color
        self.red = red
        self.green = green
        self.blue = blue
        self.alpha = alpha
        # Lets see whats the difference from current to desired index for all rgba
        red_diff = abs(self.red - self.color.rgba[0])
        green_diff = abs(self.green - self.color.rgba[1])
        blue_diff = abs(self.blue - self.color.rgba[2])
        alpha_diff = abs(self.alpha - self.color.rgba[3])
        # We need to know what's the bigger difference
        if red_diff > green_diff:
            if red_diff > blue_diff: 
                if red_diff > alpha_diff: bigger_diff = red_diff
            elif blue_diff > alpha_diff: bigger_diff = blue_diff
            else: bigger_diff = alpha_diff
        else: 
            if green_diff > blue_diff: 
                if green_diff > alpha_diff: bigger_diff = green_diff
            elif blue_diff > alpha_diff: bigger_diff = blue_diff
            else: bigger_diff = alpha_diff
        # speed is the reference speed, the value difference self.execute will
        # apply each frame for the bigger_diff.
        # So, if bigger_diff is alpha (from 0.0 to 0.8), time is 5 seconds, running at 30fps,
        # speed will be 0.8/30.0/5.0 = 0.00533..
        # If we apply a speed of 0.00533 at each frame, 30 a second, for 5 seconds, we get:
        # 0.00533*30.0*5.0 = 0.8, you see..
        speed = bigger_diff/fps / time
        
        if self.red > self.color.rgba[0]: self.red_speed = red_diff / bigger_diff * speed
        elif self.red == self.color.rgba[0]: self.red_speed = 0.0
        else: self.red_speed = -(red_diff / bigger_diff * speed)

        if self.green > self.color.rgba[1]: self.green_speed = green_diff / bigger_diff * speed
        elif self.green == self.color.rgba[1]: self.green_speed = 0.0
        else: self.green_speed = -(green_diff / bigger_diff * speed)
        
        if self.blue > self.color.rgba[2]: self.blue_speed = blue_diff / bigger_diff * speed
        elif self.blue == self.color.rgba[2]: self.blue_speed = 0.0
        else: self.blue_speed = -(blue_diff / bigger_diff * speed)
        
        if self.alpha > self.color.rgba[3]: self.alpha_speed = alpha_diff / bigger_diff * speed
        elif self.alpha == self.color.rgba[3]: self.alpha_speed = 0.0
        else: self.alpha_speed = -(alpha_diff / bigger_diff * speed)
                
        self.active = True
        actionhandler.subscribe(self)
    def execute(self):
        if self.red_speed > 0 and self.color.rgba[0] + self.red_speed > self.red: self.stop()
        elif self.red_speed < 0 and self.color.rgba[0] + self.red_speed < self.red: self.stop()
        else: 
            if self.green_speed > 0 and self.color.rgba[1] + self.green_speed > self.green: self.stop()
            elif self.green_speed < 0 and self.color.rgba[1] + self.green_speed < self.green: self.stop()
            else: 
                if self.blue_speed > 0 and self.color.rgba[2] + self.blue_speed > self.blue: self.stop()
                elif self.blue_speed < 0 and self.color.rgba[2] + self.blue_speed < self.blue: self.stop()
                else: 
                    if self.alpha_speed > 0 and self.color.rgba[3] + self.alpha_speed > self.alpha: self.stop()
                    elif self.alpha_speed < 0 and self.color.rgba[3] + self.alpha_speed < self.alpha: self.stop()
                    else: 
                        self.color.rgba = (self.color.rgba[0] + self.red_speed,
                                           self.color.rgba[1] + self.green_speed,
                                           self.color.rgba[2] + self.blue_speed,
                                           self.color.rgba[3] + self.alpha_speed)
    def stop(self):
        self.color.rgba = (self.red, self.green, self.blue, self.alpha)
        self.active = False
        actionhandler.unsubscribe(self)
    def finished(self):
        if not self.active: return True
        else: return False
        
class Rotate(object):
    """
    Rotates a qgl.scene.Group() x degrees in given axis in a certain amount
    of time
    """
    def __init__(self, group, axis=(1,0,0), degrees=180, time=1.0):
        self.group = group
        self.axis = axis
        self.degrees = degrees
        self.time = time
        
        self.active = True
        actionhandler.subscribe(self)
        
        diff = abs(self.degrees - self.group.angle)
        self.speed = diff/fps / self.time
        
        if self.degrees > self.group.angle: pass
        elif self.degrees == self.group.angle: self.stop()
        else: self.speed = -(self.speed)
        
        self.group.axis = self.axis
    def execute(self):
        if self.speed > 0 and self.group.angle + self.speed > self.degrees: self.stop()
        elif self.speed < 0 and self.group.angle - self.speed < self.degrees: self.stop()
        else:
            self.group.axis = self.axis 
            self.group.angle+=self.speed
    def stop(self):
        self.group.angle = self.degrees
        self.active = False
        actionhandler.unsubscribe(self)
    def finished(self):
        if not self.active: return True
        else: return False
        
class Scale(object):
    """
    Scales a qgl.scene.group to the desired value in a certain amount of time
    - (1.0, 1.0, 1.0) is normal size
    """
    def __init__(self, group, scale=(1.0, 1.0, 1.0), time=1.0):
        # the fade effect works like the move effect, same logic
        self.group = group
        self.xscale = scale[0]
        self.yscale = scale[1]
        self.zscale = scale[2]
        # Lets see whats the difference from current to desired index for all rgba
        xscale_diff = abs(self.xscale - self.group.scale[0])
        yscale_diff = abs(self.yscale - self.group.scale[1])
        zscale_diff = abs(self.zscale - self.group.scale[2])
        # We need to know what's the bigger difference
        if xscale_diff > yscale_diff:
            if xscale_diff > zscale_diff: bigger_diff = xscale_diff
            else: bigger_diff = zscale_diff
        else: 
            if yscale_diff > zscale_diff: bigger_diff = yscale_diff
            else: bigger_diff = zscale_diff
        # speed is the reference speed, the value difference self.execute will
        # apply each frame for the bigger_diff.
        # So, if bigger_diff is alpha (from 0.0 to 0.8), time is 5 seconds, running at 30fps,
        # speed will be 0.8/30.0/5.0 = 0.00533..
        # If we apply a speed of 0.00533 at each frame, 30 a second, for 5 seconds, we get:
        # 0.00533*30.0*5.0 = 0.8, you see..
        speed = bigger_diff/fps / time
        
        if self.xscale > self.group.scale[0]: self.xscale_speed = xscale_diff / bigger_diff * speed
        elif self.xscale == self.group.scale[0]: self.xscale_speed = 0.0
        else: self.xscale_speed = -(xscale_diff / bigger_diff * speed)

        if self.yscale > self.group.scale[1]: self.yscale_speed = yscale_diff / bigger_diff * speed
        elif self.yscale == self.group.scale[1]: self.yscale_speed = 0.0
        else: self.yscale_speed = -(yscale_diff / bigger_diff * speed)
        
        if self.zscale > self.group.scale[2]: self.zscale_speed = zscale_diff / bigger_diff * speed
        elif self.zscale == self.group.scale[2]: self.zscale_speed = 0.0
        else: self.zscale_speed = -(xscale_diff / bigger_diff * speed)
                
        self.active = True
        actionhandler.subscribe(self)
    def execute(self):
        if self.xscale_speed > 0 and self.group.scale[0] + self.xscale_speed > self.xscale: self.stop()
        elif self.xscale_speed < 0 and self.group.scale[0] + self.xscale_speed < self.xscale: self.stop()
        else: 
            if self.yscale_speed > 0 and self.group.scale[1] + self.yscale_speed > self.yscale: self.stop()
            elif self.yscale_speed < 0 and self.group.scale[1] + self.yscale_speed < self.yscale: self.stop()
            else: 
                if self.zscale_speed > 0 and self.group.scale[2] + self.zscale_speed > self.zscale: self.stop()
                elif self.zscale_speed < 0 and self.group.scale[2] + self.zscale_speed < self.zscale: self.stop()
                else: 
                    self.group.scale = (self.group.scale[0] + self.xscale_speed,
                                        self.group.scale[1] + self.yscale_speed,
                                        self.group.scale[2] + self.zscale_speed)
    def stop(self):
        self.group.scale = (self.xscale, self.yscale, self.zscale)
        self.active = False
        actionhandler.unsubscribe(self)
    def finished(self):
        if not self.active: return True
        else: return False
        
class Timer(object):
    """
    A timer attached to the fps, returns an event when finished
    """
    def __init__(self, time=1.0):
        self.time = time
        self.elapsed = 0.0
        self.active = True
        actionhandler.subscribe(self)
    def execute(self):
        if self.elapsed + 1.0/fps > self.time: self.stop()
        else:
            self.elapsed+=(1.0/fps)
    def stop(self):
        self.active = False
        actionhandler.unsubscribe(self)
    def finished(self):
        if not self.active: return True
        else: return False
        